package com.zbystudy.core;

import com.zbystudy.anno.*;
import com.zbystudy.builder.ColumnBuilder;
import com.zbystudy.builder.ForeignTableBuilder;
import com.zbystudy.constants.EngineCharator;
import com.zbystudy.core.vo.ForeignTable;
import com.zbystudy.enums.CascadeType;
import com.zbystudy.util.ArrayUtil;
import com.zbystudy.util.MapUtil;
import com.zbystudy.util.StringHelper;
import com.zbystudy.util.TypeTransformer;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import static com.zbystudy.constants.LetterChar.*;
import static com.zbystudy.util.ArrayUtil.isNotEmpty;
import static com.zbystudy.util.ClassHelper.classNameToProName;
import static com.zbystudy.util.SqlUtil.transNoAction;
import static org.apache.commons.lang3.StringUtils.*;

/**
 * Created By zby on 14:29 2019/4/2
 * 通过注解的方式创建表结构
 *
 * @author zby
 */
public class TableInit {

    /**
     * 根据数据库表名创建数据存储对象
     */
    private static Map<String, StringBuilder> dbTableMap = new LinkedHashMap<>();

    static {
        Set<String> entityFileNames = EntityPriority.sortEntityPath();
//        创建类
        for (String entityFileName : entityFileNames) {
            try {
                Class clazz = Class.forName(entityFileName);
//                获取表名
                Table dbTable = (Table) clazz.getDeclaredAnnotation(Table.class);
                if (null == dbTable) {
                    System.out.println("no table annotation found in class:" + entityFileName);
                    continue;
                }
                String tableName = dbTable.name();
                String currClazzName = clazz.getSimpleName();
                if (isBlank(tableName)) {
                    System.out.println("no table name found in class:" + entityFileName);
                    continue;
                }
                StringBuilder tableBuilder = new StringBuilder("CREATE TABLE" + BLANK_OP + BACK_QUOTE + tableName + BACK_QUOTE + LEFT_BRACKET + LINE_FEED_OP);
//                设置外键信息
//                存储的是外键信息
                Set<ForeignTable> foreignTables = new LinkedHashSet<>();
//                获取属性
                Field[] fields = clazz.getDeclaredFields();
                if (isNotEmpty(fields)) {
                    for (Field field : fields) {
//                        设置初始值
                        TableColumn tableColumn = ColumnBuilder.instance();
                        Column column = field.getDeclaredAnnotation(Column.class);
                        if (null != column) {
//                            获取字段名
                            tableColumn.setColName(isBlank(column.name()) ? field.getName() : column.name());
                            tableColumn.setColLength(column.length());
                            tableColumn.setColDefinition(column.columnDefinition());
                            tableColumn.setColNull(column.nullable() ? NULL : NOT_NULL);
                            tableColumn.setColUnique(column.unique() ? "unique" : NUL_OP);
                        }
//                        主键
                        Id id = field.getDeclaredAnnotation(Id.class);
                        if (null != id) {
                            tableColumn.setColPk("primary key");
                            tableColumn.setColPkVal(tableColumn.getColName());
                        }
//                        自动增长
                        GeneratedValue generated = field.getDeclaredAnnotation(GeneratedValue.class);
                        if (null != generated) {
                            tableColumn.setIncrementStrategy(generated.strategy().name().equals("AUTO") ? "AUTO_INCREMENT" : NUL_OP);
                        }
                        // 获取属性类型，同时获取字段长度
                        String typeName = field.getType().getSimpleName();
                        tableColumn.setColType(TypeTransformer.javaToSql(typeName));
//                        处理枚举类型
                        Enumerated enumerated = field.getDeclaredAnnotation(Enumerated.class);
                        if (null != enumerated) {
                            String enumKey = NUL_OP;
                            if ("STRING".equals(enumerated.value().name())) {
                                for (Object obj : field.getType().getEnumConstants()) {
                                    enumKey += SINGLE_QUOTES + obj.toString() + SINGLE_QUOTES + SERIES_COMMA_OP;
                                }
                            } else if ("ORDINAL".equals(enumerated.value().name())) {
                                Object[] objects = field.getType().getEnumConstants();
                                for (int i = 0; i < objects.length; i++) {
                                    enumKey += SINGLE_QUOTES + i + SINGLE_QUOTES + SERIES_COMMA_OP;
                                }
                            } else {
                                continue;
                            }
                            enumKey = substring(enumKey, 0, enumKey.lastIndexOf(SERIES_COMMA_OP));
                            tableColumn.setColType("enum" + LEFT_BRACKET + enumKey + RIGHT_BRACKET);
                        }
//                        处理多对一的关系
                        ManyToOne manyToOne = field.getDeclaredAnnotation(ManyToOne.class);
                        if (manyToOne != null) {
                            CascadeType[] cascadeTypes = manyToOne.cascade();
                            if (ArrayUtil.isEmpty(cascadeTypes)) {
                                tableColumn.setColCascade("ON DELETE CASCADE ON UPDATE CASCADE");
                            } else if (ArrayUtil.isNotEmpty(cascadeTypes) && cascadeTypes.length == 1) {
                                tableColumn.setColCascade("ON DELETE " + transNoAction(cascadeTypes[0].name()) +
                                        " ON UPDATE CASCADE");
                            } else if (ArrayUtil.isNotEmpty(cascadeTypes) && cascadeTypes.length == 2) {
                                tableColumn.setColCascade("ON DELETE " + transNoAction(cascadeTypes[0].name())
                                        + " ON UPDATE " + transNoAction(cascadeTypes[1].name()));
                            } else {
                                continue;
                            }
                        }
//                      关联表
                        JoinColumn joinColumn = field.getDeclaredAnnotation(JoinColumn.class);
                        if (null != joinColumn) {
                            ForeignTable foreignTable = ForeignTableBuilder.instance();
                            foreignTable.setForeignKeyName(joinColumn.name());
                            foreignTable.setCascade(tableColumn.getColCascade());
                            foreignTable.setForeignTableName(joinColumn.table());
                            tableColumn.setColName(joinColumn.name());
                            tableColumn.setForeignTable(joinColumn.table());
                            tableColumn.setColDefinition(joinColumn.columnDefinition());
                            tableColumn.setColNull(joinColumn.nullable() ? NULL : NOT_NULL);
//                            外键类型类型忘记填写
                            Class fieldType = Class.forName(field.getType().getName());
                            if (isBlank(tableColumn.getForeignTable())) {
                                dbTable = (Table) fieldType.getDeclaredAnnotation(Table.class);
                                if (dbTable != null && isNotBlank(dbTable.name())) {
                                    tableColumn.setForeignTable(dbTable.name());
                                    foreignTable.setForeignTableName(tableColumn.getForeignTable());
                                }
                            }
                            foreignTable.setForeignName("fk" + UNDERLINE + classNameToProName(fieldType.getSimpleName()) +
                                    UNDERLINE + classNameToProName(currClazzName));
                            fields = fieldType.getDeclaredFields();
                            if (ArrayUtil.isNotEmpty(fields)) {
                                for (Field fkField : fields) {
                                    id = fkField.getDeclaredAnnotation(Id.class);
                                    if (null != id) {
                                        tableColumn.setColType(TypeTransformer.javaToSql(fkField.getType().getSimpleName()));
                                        column = fkField.getDeclaredAnnotation(Column.class);
//                                    设置外键表的关联字段
                                        foreignTable.setForeignTablePk(null != column && isBlank(column.name()) ? column.name() : fkField.getName());
                                    }
                                }
                            }
                            foreignTables.add(foreignTable);
                        }
//                      处理columnDefinition = "int(11) NOT NULL COMMENT '外键是学生表'"和真实的字段
                        String colType = tableColumn.getColType();
                        String colDefinition = tableColumn.getColDefinition();
                        if (isNotBlank(colDefinition) && isNotBlank(colType)) {
                            String[] sqlNumberType = {"INT(11)", "INTEGER(11)", "BIGINT(20)", "VARCHAR(255)", "SMALLINT(6)", "NUMERIC(10)", "TINYINT(4)", "BIT(1)"};
                            if (ArrayUtils.contains(sqlNumberType, colType)) {
                                int colNum = Integer.parseInt(substring(colType, colType.indexOf(LEFT_BRACKET) + 1, colType.lastIndexOf(RIGHT_BRACKET)));
                                colType = substring(colType, 0, colType.lastIndexOf(LEFT_BRACKET));
                                if (StringUtils.containsAny(colDefinition, colType)) {
                                    int typeEndLength = StringHelper.getEndLength(colDefinition, colType);
//                                    COL_DEFINITION包含字符串，且同时包含(  )
                                    if (typeEndLength != 0 && StringUtils.containsAny(colDefinition, LEFT_BRACKET, RIGHT_BRACKET)) {
                                        String definitionNum = StringUtils.substring(colDefinition, typeEndLength + 1, colDefinition.indexOf(")"));
                                        if (Integer.parseInt(definitionNum) <= colNum) {
                                            tableColumn.setColType(colType + LEFT_BRACKET + Integer.parseInt(definitionNum) + RIGHT_BRACKET);
                                        }
                                        tableColumn.setColDefinition(StringUtils.remove(colDefinition, tableColumn.getColType()));
                                    }
                                }
                            }
                        }
                        tableBuilder.append(TAB_OP + BLANK_OP + tableColumn.getColName() + BLANK_OP
                                + tableColumn.getColType() + BLANK_OP
                                + tableColumn.getColNull() + BLANK_OP
                                + (tableColumn.getIncrementStrategy() != null ? tableColumn.getIncrementStrategy() + BLANK_OP : NUL_OP)
                                + (tableColumn.getColDefinition() != null ? tableColumn.getColDefinition() + BLANK_OP : NUL_OP)
                                + (tableColumn.getColPk() != null ? tableColumn.getColPk() : NUL_OP)
                                + SERIES_COMMA_OP + LINE_FEED_OP
                        );
                    }
                }
                if (foreignTables.size() > 0) {
                    for (ForeignTable foreignTable : foreignTables) {
                        tableBuilder.append(TAB_OP + BLANK_OP + "CONSTRAINT" + BLANK_OP +
                                foreignTable.getForeignName() + BLANK_OP + "FOREIGN KEY" + LEFT_BRACKET + BACK_QUOTE +
                                foreignTable.getForeignKeyName() + BACK_QUOTE + RIGHT_BRACKET + BLANK_OP + "REFERENCES" + BLANK_OP + BACK_QUOTE +
                                foreignTable.getForeignTableName() + BACK_QUOTE + LEFT_BRACKET + BACK_QUOTE +
                                foreignTable.getForeignTablePk() + BACK_QUOTE + RIGHT_BRACKET + BLANK_OP +
                                foreignTable.getCascade() + SERIES_COMMA_OP + LINE_FEED_OP
                        );
                    }
                }
                tableBuilder = new StringBuilder(tableBuilder.substring(0, tableBuilder.lastIndexOf(SERIES_COMMA_OP)));
                tableBuilder.append(LINE_FEED_OP + BLANK_OP + RIGHT_BRACKET + BLANK_OP
                        + EngineCharator.tablEengine + BLANK_OP + EngineCharator.tableCharacter);
                if (MapUtil.existKey(tableName, dbTableMap)) {
                    continue;
                }
                dbTableMap.put(tableName, tableBuilder);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Created By zby on 16:09 2019/4/3
     * 获取字段表
     */
    public static Map<String, StringBuilder> getDbTableMap() {
        return dbTableMap;
    }
}


